home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / POV-Ray 3.0.2 / src / SOURCE / PNG_POV.C < prev    next >
Encoding:
C/C++ Source or Header  |  1997-01-24  |  50.0 KB  |  1,690 lines  |  [TEXT/CWIE]

  1. /*****************************************************************************
  2. *        png_pov.c
  3. *
  4. *  This module contains the code to read and write the PNG output file
  5. *
  6. *  from Persistence of Vision(tm) Ray Tracer
  7. *  Copyright 1996 Persistence of Vision Team
  8. *---------------------------------------------------------------------------
  9. *  NOTICE: This source code file is provided so that users may experiment
  10. *  with enhancements to POV-Ray and to port the software to platforms other
  11. *  than those supported by the POV-Ray Team.  There are strict rules under
  12. *  which you are permitted to use this file.  The rules are in the file
  13. *  named POVLEGAL.DOC which should be distributed with this file. If
  14. *  POVLEGAL.DOC is not available or for more info please contact the POV-Ray
  15. *  Team Coordinator by leaving a message in CompuServe's Graphics Developer's
  16. *  Forum.  The latest version of POV-Ray may be found there as well.
  17. *
  18. * This program is based on the popular DKB raytracer version 2.12.
  19. * DKBTrace was originally written by David K. Buck.
  20. * DKBTrace Ver 2.0-2.12 were written by David K. Buck & Aaron A. Collins.
  21. *
  22. * Original patch copyright 1995 Andreas Dilger 
  23. * Updated for POV 3.0 by Tim Wegner, August 1995.
  24. * Updated to allow resuming by Andreas Dilger, Sept 1995.
  25. * Updated to support Alpha channel input/output by Andreas Dilger, Sept 1995
  26. * Updated to set the flush distance based on the file buffer size, Dec 1995
  27. * Updated to use the libpng 0.87 messaging functions, Dec 1995
  28. * Updated to use the libpng 0.89 structure interface, Jun 1996
  29. *
  30. *****************************************************************************/
  31.  
  32. /*****************************************************************************
  33. *  This code requires the use of libpng, Group 42's PNG reference library.
  34. *  libpng is  Copyright (c) 1995 Guy Eric Schalnat, Group 42, Inc.
  35. *
  36. *  This code also requires the use of Zlib,
  37. *  Zlib is Copyright (C) 1995 Jean-loup Gailly and Mark Adler
  38. *
  39. *  The latest version of these libraries are available at ftp.uu.net as
  40. *
  41. *  /graphics/png/libpngXX.tar.gz.
  42. *  /archiver/zlib/zlib-XX.tar.gz.
  43. *
  44. *  where XX is the latest version of the library.
  45. *
  46. *****************************************************************************/
  47.  
  48. #include "frame.h"
  49. #include "povproto.h"
  50. #include "povray.h"
  51. #include "optout.h"
  52. #include "png.h"
  53. #include "png_pov.h"
  54.  
  55.  
  56.  
  57. /*****************************************************************************
  58. * Local preprocessor defines
  59. ******************************************************************************/
  60.  
  61. /* Number of scanlines between output flushes, and hence the maximum number of
  62.  * lines lost for an interrupted render.  Note that making it much smaller
  63.  * than about 10 for a 640x480 image will noticably degrade compression.
  64.  * If a very small buffer is specified, we don't want to flush more than once
  65.  * every 10 lines or so (assuming a 2:1 compression ratio).
  66.  */
  67. /*
  68. #define FLUSH_DIST 10
  69. #define FLUSH_DIST (*width >= 640 ? 10 : 6400 / *width)
  70. */
  71. #define FLUSH_DIST ((opts.Options & BUFFERED_OUTPUT && \
  72.                      handle->buffer_size > (*width * png_stride * 5)) ? \
  73.                     (handle->buffer_size / (*width * png_stride)): \
  74.                     (*width >= 640 ? 10 : 6400 / *width))
  75.  
  76. #define NTEXT 15     /* Maximum number of tEXt comment blocks */
  77. #define MAXTEXT 1024 /* Maximum length of a tEXt message */
  78.  
  79.  
  80.  
  81. /*****************************************************************************
  82. * Local typedefs
  83. ******************************************************************************/
  84.  
  85.  
  86.  
  87. /*****************************************************************************
  88. * Local variables
  89. ******************************************************************************/
  90.  
  91. static png_struct *png_ptr  = NULL;
  92. static png_info   *info_ptr = NULL;
  93. static png_struct *o_png_ptr  = NULL;
  94. static png_byte   *row_ptr  = NULL;
  95. static int        png_stride;
  96. static char       tmp_fname[FILE_NAME_LENGTH];
  97. static FILE       *tmp_fp = NULL;
  98.  
  99.  
  100.  
  101. /*****************************************************************************
  102. * Static functions
  103. ******************************************************************************/
  104.  
  105. static int Open_Png_File PARAMS((FILE_HANDLE *handle, char *name, int *width,
  106.                               int *height, int buffer_size, int mode));
  107. static void Write_Png_Line PARAMS((FILE_HANDLE *handle, COLOUR *line_data,
  108.                                    int line_number));
  109. static int Read_Png_Line PARAMS((FILE_HANDLE *handle, COLOUR *line_data,
  110.                                  int *line_number));
  111. static void Close_Png_File PARAMS((FILE_HANDLE *handle));
  112.  
  113.  
  114. /* These are replacement error and warning functions for the libpng code */
  115. static void png_pov_err PARAMS((png_structp, png_const_charp));
  116. static void png_pov_warn PARAMS((png_structp, png_const_charp));
  117.  
  118. /* This is an internal function for libpng */
  119. extern void png_write_finish_row PARAMS((png_structp));
  120.  
  121. /*****************************************************************************
  122. *
  123. * FUNCTION      : Get_Png_File_Handle
  124. *
  125. * ARGUMENTS     : none
  126. *
  127. * MODIFIED ARGS : none
  128. *
  129. * RETURN VALUE  : File Handle
  130. *
  131. * AUTHOR        : Andreas Dilger
  132. *
  133. * DESCRIPTION
  134. *
  135. *   Allocate memory for and setup file handle for PNG file.
  136. *
  137. * CHANGES
  138. *
  139. *   Updated for POV-Ray 3.X - [TIW]
  140. *
  141. ******************************************************************************/
  142.  
  143. FILE_HANDLE *Get_Png_File_Handle()
  144. {
  145.   FILE_HANDLE *handle;
  146.   
  147.   handle = (FILE_HANDLE *)POV_MALLOC(sizeof(FILE_HANDLE), "PNG file handle");
  148.  
  149.   handle->Open_File_p  = Open_Png_File;
  150.   handle->Write_Line_p = Write_Png_Line;
  151.   handle->Read_Line_p  = Read_Png_Line;
  152.   handle->Read_Image_p = Read_Png_Image;
  153.   handle->Close_File_p = Close_Png_File;
  154.   
  155.   handle->buffer_size = 0;
  156.   
  157.   handle->file = NULL;
  158.   handle->buffer = NULL;
  159.   
  160.   return (handle);
  161. }
  162.  
  163.  
  164. /*****************************************************************************
  165. *
  166. * FUNCTION      : png_pov_warn
  167. *
  168. * ARGUMENTS     : png_struct *png_ptr; char *msg;
  169. *
  170. * MODIFIED ARGS :
  171. *
  172. * RETURN VALUE  :
  173. *
  174. * AUTHOR        : Andreas Dilger
  175. *
  176. * DESCRIPTION
  177. *
  178. *   Prints an warning message using the POV I/O functions.  This uses the
  179. *   png io_ptr to determine whether error messages should be printed or
  180. *   not.
  181. *
  182. * CHANGES
  183. *
  184. ******************************************************************************/
  185. static void png_pov_warn(png_ptr, msg)
  186. png_structp png_ptr;
  187. png_const_charp msg;
  188. {
  189.   if (png_get_error_ptr(png_ptr))
  190.     Warning(0.0,"libpng: %s\n",msg);
  191. }
  192.  
  193.  
  194. /*****************************************************************************
  195. *
  196. * FUNCTION      : png_pov_err
  197. *
  198. * ARGUMENTS     : png_struct *png_ptr; char *msg;
  199. *
  200. * MODIFIED ARGS :
  201. *
  202. * RETURN VALUE  :
  203. *
  204. * AUTHOR        : Andreas Dilger
  205. *
  206. * DESCRIPTION
  207. *
  208. *   If the png io_ptr is TRUE, this prints an error message using the POV
  209. *   I/O function.  It will return to the location of the last setjmp call
  210. *   for this stream in any case.
  211. *
  212. * CHANGES
  213. *
  214. ******************************************************************************/
  215. static void png_pov_err(png_ptr, msg)
  216. png_structp png_ptr;
  217. png_const_charp msg;
  218. {
  219.   if (png_get_error_ptr(png_ptr))
  220.     Error_Line("libpng: %s\n",msg);
  221.  
  222.   longjmp(png_ptr->jmpbuf,1);
  223. }
  224.  
  225.  
  226. /*****************************************************************************
  227. *
  228. * FUNCTION      : Open_Png_File
  229. *
  230. * ARGUMENTS     : FILE_HANDLE *handle; char *name; int *width; int *height;
  231. *                 int buffer_size; int mode;
  232. *
  233. * MODIFIED ARGS : handle, width, height
  234. *
  235. * RETURN VALUE  : 1 or 0 for success or failure
  236. *
  237. * AUTHOR        : Andreas Dilger
  238. *
  239. * DESCRIPTION
  240. *
  241. *   Open a PNG file and allocate the needed PNG structure buffers
  242. *
  243. * CHANGES
  244. *
  245. *   Updated for POV-Ray 3.X - [TIW]
  246. *   Updated to handle resuming interrupted traces, Sept 1995 - [AED]
  247. *   Updated to output grayscale heightfield if requested - [AED]
  248. *   Updated to allow grayscale and alpha together, Oct 1995 - [AED]
  249. *   Updated to write gamma differently based on file type, Nov 1995 - [AED]
  250. *   Changed temp file name from TEMP_FILE_BASE to scene name, Feb 1996 - [AED]
  251. *   Changed temp file from scene name to path + scene name, Jun 1996 - [AED]
  252. *
  253. ******************************************************************************/
  254.  
  255. static int Open_Png_File(handle, name, width, height, buffer_size, mode)
  256. FILE_HANDLE *handle;
  257. char *name;
  258. int *width;
  259. int *height;
  260. int buffer_size;
  261. int mode;
  262. {
  263.   handle->mode = mode;
  264.   handle->filename = name;
  265.   
  266.   switch (mode)
  267.   {
  268.     case READ_MODE:
  269.     
  270.       /* We can't resume from stdout. */
  271.       if (opts.Options & TO_STDOUT)
  272.       {
  273.         Status_Info("\n");
  274.         return(0);
  275.       }
  276.  
  277.       /* Initialize PNG output routines using temporary file name.  We
  278.        * need to use the path, or the rename will fail if the temp file
  279.        * is not on the same drive as the output file.
  280.        */
  281.       sprintf(tmp_fname, "%s%s.tpn", opts.Output_Path, opts.Scene_Name);
  282.  
  283.       /* Move the old output file to a temporary file, so it can be
  284.        * read in and simultaneously written out to the new output file.
  285.        * Note that this can potentially be destructive, but it is
  286.        * impossible to change the output stream in mid-write.  We have
  287.        * to check if a temp file already exists, in case the transfer
  288.        * has been previously aborted.
  289.        */
  290.       if ((tmp_fp = fopen(tmp_fname,READ_FILE_STRING)) == NULL)
  291.       {
  292.         /* The temp file doesn't exist.  Try the original file. */
  293.         if ((tmp_fp = fopen(name,READ_FILE_STRING)) == NULL)
  294.         {
  295.           Status_Info("\n");
  296.           return(0);  /* Neither file exists - start from scratch. */
  297.         }
  298.         else /* The original file exists, but the temp file doesn't. */
  299.         {
  300.           fclose(tmp_fp);
  301.  
  302.           if (RENAME_FILE(name,tmp_fname) == RENAME_FILE_ERR)
  303.           {
  304.             Error("\nError making temporary PNG file for continuing trace.\n");
  305.           }
  306.  
  307.           /* Open the original file (now with a new name) for reading */
  308.           if ((tmp_fp = fopen(tmp_fname,READ_FILE_STRING)) == NULL)
  309.           {
  310.             RENAME_FILE(tmp_fname,name); /* Try to rename back - not crucial */
  311.             Error("\nError opening temporary PNG file for continuing trace.\n");
  312.           }
  313.         }
  314.       }
  315.       /* The temp file already exists.  If we can open the original file
  316.        * as well, then there is something wrong, and we can't automatically
  317.        * defide which file to delete.
  318.        */
  319.       else if((handle->file = fopen(name,READ_FILE_STRING)) != NULL)
  320.       {
  321.         fclose(tmp_fp);
  322.         fclose(handle->file);
  323.  
  324.         Error_Line("\nBoth original and temporary PNG files exist after an interrupted trace.\n");
  325.         Error("Please delete either %s or %s (preferrably the smaller).\n",name,tmp_fname);
  326.       }
  327.  
  328.       /* Try to open the new output file for writing.  If we can't, try
  329.        * to move the old one back so that users don't fret if it's missing.
  330.        * PNG will be able to continue without loss of data either way.
  331.        */
  332.       if ((handle->file = fopen(name, WRITE_FILE_STRING)) == NULL)
  333.       {
  334.         Status_Info("\n");
  335.  
  336.         fclose(tmp_fp);
  337.         RENAME_FILE(tmp_fname,name);
  338.         return(-1);
  339.       }
  340.  
  341.       if (buffer_size > 0)
  342.       {
  343.         handle->buffer = POV_MALLOC(buffer_size, "PNG file buffer");
  344.         setvbuf(handle->file, handle->buffer, _IOFBF, buffer_size);
  345.       }
  346.  
  347.       handle->buffer_size = buffer_size;
  348.  
  349.       /* The original input file */
  350.       if ((o_png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
  351.                        (png_voidp)FALSE, png_pov_err, png_pov_warn)) == NULL ||
  352.           (info_ptr = png_create_info_struct(o_png_ptr)) == NULL)
  353.       {
  354.         Error("Error allocating PNG data structures");
  355.       }
  356.  
  357.       if (setjmp(o_png_ptr->jmpbuf))
  358.       {
  359.         /* If we get here, we had a problem reading the file */
  360.         Status_Info("\n");
  361.  
  362.         if (handle->buffer != NULL)
  363.         {
  364.           POV_FREE(handle->buffer);
  365.           handle->buffer = NULL;
  366.         }
  367.  
  368.         png_destroy_read_struct(&o_png_ptr, &info_ptr, (png_infopp)NULL);
  369.  
  370.         fclose(handle->file);
  371.         handle->file = NULL;
  372.         fclose(tmp_fp);
  373.         tmp_fp = NULL;
  374.  
  375.         return(0);
  376.       }
  377.  
  378.       /* Set up the compression structure */
  379.       png_init_io(o_png_ptr, tmp_fp);
  380.  
  381.       /* Read in header info from the file */
  382.       png_read_info(o_png_ptr, info_ptr);
  383.  
  384.       if (info_ptr->color_type & ~(PNG_COLOR_MASK_COLOR | PNG_COLOR_MASK_ALPHA))
  385.       {
  386.         return(0);
  387.       }
  388.  
  389.       Status_Info("\nResuming interrupted trace from %s",handle->filename);
  390.  
  391.       /* The new output file.  Thank god for re-entrant libpng/libz code! */
  392.       if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
  393.                      (png_voidp)TRUE, png_pov_err, png_pov_warn)) == NULL)
  394.       {
  395.         Error("Error allocating PNG data structures");
  396.       }
  397.  
  398.       if (setjmp(png_ptr->jmpbuf))
  399.       {
  400.         /* If we get here, we had a problem writing the file */
  401.         Status_Info("\n");
  402.  
  403.         if (handle->buffer != NULL)
  404.         {
  405.           POV_FREE(handle->buffer);
  406.           handle->buffer = NULL;
  407.         }
  408.  
  409.         png_destroy_read_struct(&o_png_ptr, &info_ptr, (png_infopp)NULL);
  410.         png_destroy_write_struct(&png_ptr, (png_infopp)NULL);
  411.  
  412.         fclose(handle->file);
  413.         handle->file = NULL;
  414.         fclose(tmp_fp);
  415.         tmp_fp = NULL;
  416.  
  417.         if (DELETE_FILE(name) != DELETE_FILE_ERR)
  418.         {
  419.           RENAME_FILE(tmp_fname,name);  /* Try to get the original file back */
  420.         }
  421.  
  422.         return(-1);
  423.       }
  424.  
  425.       /* Set up the compression structure */
  426.       png_init_io(png_ptr, handle->file);
  427.  
  428.       /* Fill in the relevant image information from the resumed file */
  429.       *width = handle->width = info_ptr->width;
  430.       *height = handle->height = info_ptr->height;
  431.  
  432.       /* Find out if file is a valid format, and if it had Alpha in it */
  433.       if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  434.       {
  435.         opts.Options |= OUTPUT_ALPHA;
  436.       }
  437.  
  438.       if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
  439.       {
  440.         opts.Options |= HF_GRAY_16;
  441.         opts.PaletteOption = GREY;       /* Force grayscale preview */
  442.       }
  443.  
  444. #if defined(PNG_READ_sBIT_SUPPORTED)
  445.       if (info_ptr->valid & PNG_INFO_sBIT)
  446.       {
  447.         if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
  448.         {
  449.           opts.OutputQuality = info_ptr->sig_bit.red;
  450.         }
  451.         else
  452.         {
  453.           opts.OutputQuality = info_ptr->sig_bit.gray;
  454.         }
  455.       }
  456.  
  457. #else /* !PNG_READ_sBIT_SUPPORTED */
  458.       if (info_ptr->bit_depth == 8 && opts.OutputQuality > 8 ||
  459.           info_ptr->bit_depth == 16 && opts.OutputQuality <= 8)
  460.       {
  461.         Error("\nSpecified color depth +fn%d not the same as depth %d in %s\n",
  462.               opts.OutputQuality, info_ptr->bit_depth, name);
  463.       }
  464. #endif /* !PNG_READ_sBIT_SUPPORTED */
  465.  
  466. #if defined(PNG_READ_oFFs_SUPPORTED)
  467.       opts.First_Column = info_ptr->x_offset;
  468.       opts.First_Line   = info_ptr->y_offset;
  469. #endif /* PNG_READ_oFFs_SUPPORTED */
  470.  
  471.       png_write_info(png_ptr, info_ptr);
  472.  
  473.       png_stride = info_ptr->color_type & PNG_COLOR_MASK_COLOR ? 3 : 1;
  474.  
  475.       if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  476.         png_stride++;
  477.  
  478.       png_stride *= (opts.OutputQuality + 7) / 8;
  479.  
  480.       row_ptr = (png_byte *)POV_MALLOC(*width*png_stride,"PNG read row buffer");
  481.       break;
  482.  
  483.     case WRITE_MODE:
  484.  
  485.       if (opts.Options & TO_STDOUT)
  486.       {
  487.         buffer_size = 0;
  488.         handle->file = stdout;
  489.       }
  490.       else if ((handle->file = fopen(name, WRITE_FILE_STRING)) == NULL)
  491.       {
  492.         return(0);
  493.       }
  494.  
  495.       if (buffer_size != 0)
  496.       {
  497.         handle->buffer = POV_MALLOC((size_t)buffer_size, "PNG file buffer");
  498.         setvbuf(handle->file, handle->buffer, _IOFBF, buffer_size);
  499.       }
  500.  
  501.       handle->buffer_size = buffer_size;
  502.  
  503.       if ((png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING,
  504.                      (png_voidp)TRUE, png_pov_err, png_pov_warn)) == NULL ||
  505.           (info_ptr = png_create_info_struct(png_ptr)) == NULL)
  506.       {
  507.         Error("Error allocating PNG data structures");
  508.       }
  509.  
  510.       if (setjmp(png_ptr->jmpbuf))
  511.       {
  512.         /* If we get here, we had a problem writing the file */
  513.         if (handle->buffer != NULL)
  514.         {
  515.           POV_FREE(handle->buffer);
  516.           handle->buffer = NULL;
  517.         }
  518.  
  519.         png_destroy_write_struct(&png_ptr, &info_ptr);
  520.  
  521.         fclose(handle->file);
  522.         handle->file = NULL;
  523.  
  524.         return(0);
  525.       }
  526.  
  527.       /* Set up the compression structure */
  528.  
  529.       png_init_io(png_ptr, handle->file);
  530.  
  531.       /* Fill in the relevant image information */
  532.  
  533.       info_ptr->width = handle->width = *width;
  534.       info_ptr->height = handle->height = *height;
  535.  
  536.       info_ptr->bit_depth = 8 * ((opts.OutputQuality + 7) / 8);
  537.  
  538.       if (opts.Options & HF_GRAY_16)
  539.       {
  540.         info_ptr->color_type = PNG_COLOR_TYPE_GRAY;
  541.       }
  542.       else
  543.       {
  544.         info_ptr->color_type = PNG_COLOR_TYPE_RGB;
  545.       }
  546.  
  547.       if (opts.Options & OUTPUT_ALPHA)
  548.       {
  549.         info_ptr->color_type |= PNG_COLOR_MASK_ALPHA;
  550.       }
  551.  
  552. #if defined(PNG_WRITE_sBIT_SUPPORTED)
  553.       if (info_ptr->color_type & PNG_COLOR_MASK_COLOR)
  554.       {
  555.         info_ptr->sig_bit.red =
  556.         info_ptr->sig_bit.green =
  557.         info_ptr->sig_bit.blue = opts.OutputQuality;
  558.       }
  559.       else
  560.       {
  561.         info_ptr->sig_bit.gray = opts.OutputQuality;
  562.       }
  563.  
  564.       if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  565.       {
  566.         info_ptr->sig_bit.alpha = opts.OutputQuality;
  567.       }
  568.  
  569.       info_ptr->valid |= PNG_INFO_sBIT;
  570. #endif /* PNG_WRITE_sBIT_SUPPORTED */
  571.  
  572. #if defined(PNG_WRITE_gAMA_SUPPORTED)
  573.       if (handle->file_type & (IMAGE_FTYPE | GRAY_FTYPE))
  574.       {
  575.         info_ptr->gamma = 1.0/opts.DisplayGamma;
  576.         info_ptr->valid |= PNG_INFO_gAMA;
  577.       }
  578.       else if (handle->file_type & (HIST_FTYPE | HF_FTYPE))
  579.       {
  580.         info_ptr->gamma = 1.0;
  581.         info_ptr->valid |= PNG_INFO_gAMA;
  582.       }
  583. #endif /* PNG_WRITE_gAMA_SUPPORTED */
  584.  
  585. #if defined(PNG_WRITE_oFFs_SUPPORTED)
  586.       if (opts.First_Column != 0 || opts.First_Line != 0)
  587.       {
  588.         info_ptr->x_offset = opts.First_Column;
  589.         info_ptr->y_offset = opts.First_Line;
  590.  
  591.         info_ptr->offset_unit_type = PNG_OFFSET_PIXEL;
  592.  
  593.         info_ptr->valid |= PNG_INFO_oFFs;
  594.       }
  595. #endif /* PNG_WRITE_oFFs_SUPPORTED */
  596.  
  597.       if (handle->file_type & HIST_FTYPE)
  598.       {
  599.       /* If we are writing a histogram file, we could potentially output
  600.        * a pCAL chunk with the max histogram value, to allow recovery of
  601.        * the original timing data.  However, pCAL is not yet official at
  602.        * the time of this writing.
  603.        */
  604.       }
  605.  
  606.       png_write_info(png_ptr, info_ptr);
  607.  
  608.       png_stride = info_ptr->color_type & PNG_COLOR_MASK_COLOR ? 3 : 1;
  609.  
  610.       if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  611.         png_stride++;
  612.  
  613.       png_stride *= (opts.OutputQuality + 7) / 8;
  614.  
  615.       row_ptr = (png_byte *)POV_MALLOC(*width*png_stride, "PNG write row buffer");
  616.  
  617. #if defined(PNG_WRITE_FLUSH_SUPPORTED)
  618.       /* Set libpng to flush the output buffers every few lines, so that
  619.        * in case of a rude crash we don't lose very much data.
  620.        */
  621.       png_set_flush(png_ptr, FLUSH_DIST);
  622. #endif /* PNG_WRITE_FLUSH_SUPPORTED */
  623.  
  624.       break;
  625.  
  626.     case APPEND_MODE:
  627.  
  628. #if defined(PNG_WRITE_FLUSH_SUPPORTED)
  629.       if (setjmp(png_ptr->jmpbuf))
  630.       {
  631.         /* If we get here, we had a problem writing the file */
  632.  
  633.         if (handle->buffer != NULL)
  634.         {
  635.           POV_FREE(handle->buffer);
  636.           handle->buffer = NULL;
  637.         }
  638.  
  639.         png_destroy_write_struct(&png_ptr, &info_ptr);
  640.  
  641.         fclose(handle->file);
  642.         handle->file = NULL;
  643.  
  644.         return(0);
  645.       }
  646.  
  647.       /* Write out the data in the PNG/zlib buffers, and set automatic
  648.        * flushing for every few scanlines, in case of a rude crash.
  649.        */
  650.       png_write_flush(png_ptr);
  651.       png_set_flush(png_ptr, FLUSH_DIST);
  652. #else  /* !PNG_WRITE_FLUSH_SUPPORTED */
  653.       fflush(handle->file);
  654. #endif /* PNG_WRITE_FLUSH_SUPPORTED */
  655.  
  656.       if (!(opts.Options & TO_STDOUT) && (handle->file =
  657.           freopen(name, APPEND_FILE_STRING, handle->file)) == NULL)
  658.       {
  659.         if (handle->buffer != NULL)
  660.         {
  661.           POV_FREE(handle->buffer);
  662.           handle->buffer = NULL;
  663.         }
  664.  
  665.         png_destroy_write_struct(&png_ptr, &info_ptr);
  666.  
  667.         return(0);
  668.       }
  669.  
  670.       /* Delete the temporary data file.  Note that the new output file
  671.        * is all ready to go - nothing needs to be done here.
  672.        */
  673.       if (tmp_fp != NULL)
  674.       {
  675.         fclose(tmp_fp);
  676.         tmp_fp = NULL;
  677.  
  678.         if (DELETE_FILE(tmp_fname) == DELETE_FILE_ERR)
  679.         {
  680.           Warning(0.0,"Can't delete temporary PNG file %s.  Please delete it.\n",tmp_fname);
  681.         }
  682.       }
  683.   }
  684.  
  685.   return(1);
  686. }
  687.  
  688.  
  689.  
  690. /*****************************************************************************
  691. *
  692. * FUNCTION      : Write_Png_Line
  693. *
  694. * ARGUMENTS     : handle, line_data, line_number
  695. *
  696. * MODIFIED ARGS : none
  697. *
  698. * RETURN VALUE  : none
  699. *
  700. * AUTHOR        : Andreas Dilger
  701. *
  702. * DESCRIPTION
  703. *
  704. *   Write a line of data to the PNG file
  705. *
  706. * CHANGES
  707. *
  708. *   Updated for POV-Ray 3.X - [TIW]
  709. *   Updated to do flush output to reduce data loss - [AED]
  710. *   Updated to output Alpha channel if requested - [AED]
  711. *   Updated to output grayscale heightfield if requested - [AED]
  712. *   Updated to allow grayscale in 5-8 bpp if desired, Oct 1995 - [AED]
  713. *   Updated to allow grayscale and alpha together, Oct 1995 - [AED]
  714. *   Changed how bit-depths 9-15 get promoted to 16 bits based on new
  715. *     recommendations from the PNG Group,  Nov 1995 - [AED]
  716. *
  717. ******************************************************************************/
  718.  
  719. static void Write_Png_Line(handle, line_data, line_number)
  720. FILE_HANDLE *handle;
  721. COLOUR *line_data;
  722. int line_number;
  723. {
  724.   register int col, j;
  725.   int himask;
  726.   int color;
  727.  
  728.  
  729.   /*
  730.    * We must copy all the values because PNG expects RGBRGB bytes, but
  731.    * POV-Ray stores RGB components in separate arrays as floats.  In
  732.    * order to use the full scale values at the lower bit depth, PNG
  733.    * recommends filling the low-order bits with a copy of the high-order
  734.    * bits.  However, studies have shown that filling the low order bits
  735.    * with constant bits significantly improves compression, which I'm
  736.    * doing here.  Note that since the true bit depth is stored in the
  737.    * sBIT chunk, the extra packed bits are not important.
  738.    */
  739.  
  740.   switch (opts.OutputQuality)
  741.   {
  742.     case 5:
  743.     case 6:
  744.     case 7:
  745.       /* Handle shifting for arbitrary output bit depth */
  746.  
  747.       himask = 0xFF ^ ((1 << (8 - opts.OutputQuality)) - 1);
  748.  
  749.       if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
  750.       {
  751.         for (col = j = 0; col < handle->width; col++, j += png_stride)
  752.         {
  753.           color = (png_byte)floor((line_data[col][RED]*0.30 +
  754.                                    line_data[col][GREEN]*0.59 +
  755.                                    line_data[col][BLUE]*0.11) * 255.0);
  756.  
  757.           /* Use left-bit replication (LBR) for bit depths < 8 */
  758.           row_ptr[j] = color & himask;
  759.           row_ptr[j] |= color >> opts.OutputQuality;
  760.  
  761.           /* Handle Alpha here if needed - must use exact bit replication
  762.            * instead of truncation or 100... termination
  763.            */
  764.           if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  765.           {
  766.             color = 255 - (int)floor(line_data[col][TRANSM] * 255.0);
  767.  
  768.             row_ptr[j + 1] = color & himask;
  769.             row_ptr[j + 1] |= color >> opts.OutputQuality;
  770.           }
  771.         }
  772.       }
  773.       else
  774.       {
  775.         for (col = j = 0; col < handle->width; col++, j += png_stride)
  776.         {
  777.           color = (int)floor(line_data[col][RED]   * 255.0);
  778.  
  779.           row_ptr[j] = color & himask;
  780.           row_ptr[j] |= color >> opts.OutputQuality;
  781.  
  782.           color = (int)floor(line_data[col][GREEN] * 255.0);
  783.  
  784.           row_ptr[j + 1] = color & himask;
  785.           row_ptr[j + 1] |= color >> opts.OutputQuality;
  786.  
  787.           color = (int)floor(line_data[col][BLUE]  * 255.0);
  788.  
  789.           row_ptr[j + 2] = color & himask;
  790.           row_ptr[j + 2] |= color >> opts.OutputQuality;
  791.  
  792.           /* Handle Alpha here if needed */
  793.           if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  794.           {
  795.             color = 255 - (int)floor(line_data[col][TRANSM] * 255.0);
  796.  
  797.             row_ptr[j + 3] = color & himask;
  798.             row_ptr[j + 3] |= color >> opts.OutputQuality;
  799.           }
  800.         }
  801.       }
  802.       break;
  803.  
  804.     case 8:
  805.       if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
  806.       {
  807.         for (col = j = 0; col < handle->width; col++, j += png_stride)
  808.         {
  809.           row_ptr[j] = (png_byte)floor((line_data[col][RED]*0.30 +
  810.                                         line_data[col][GREEN]*0.59 +
  811.                                         line_data[col][BLUE]*0.11) * 255.0);
  812.  
  813.           /* Handle Alpha here if needed */
  814.           if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  815.           {
  816.             row_ptr[j+1] = (png_byte)(255-floor(line_data[col][TRANSM]*255.0));
  817.           }
  818.         }
  819.       }
  820.       else
  821.       {
  822.         for (col = j = 0; col < handle->width; col++, j += png_stride)
  823.         {
  824.           row_ptr[j] = (png_byte)floor(line_data[col][RED]   * 255.0);
  825.           row_ptr[j + 1] = (png_byte)floor(line_data[col][GREEN] * 255.0);
  826.           row_ptr[j + 2] = (png_byte)floor(line_data[col][BLUE]  * 255.0);
  827.  
  828.           /* Handle Alpha here if needed */
  829.           if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  830.           {
  831.             row_ptr[j+3] = (png_byte)(255-floor(line_data[col][TRANSM]*255.0));
  832.           }
  833.         }
  834.       }
  835.       break;
  836.  
  837.     case 16:
  838.       if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
  839.       {
  840.         for (col = j = 0; col < handle->width; col++, j += png_stride)
  841.         {
  842.           color = (int)floor((line_data[col][RED]*0.30 +
  843.                               line_data[col][GREEN]*0.59 +
  844.                               line_data[col][BLUE]*0.11) * 65535.0);
  845.  
  846.           row_ptr[j] = color >> 8;
  847.           row_ptr[j + 1] = color & 0xFF;
  848.  
  849.           /* Handle Alpha here if needed */
  850.           if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  851.           {
  852.             color = 65535 - (int)floor(line_data[col][TRANSM]  * 65535.0);
  853.  
  854.             row_ptr[j + 2] = color >> 8;
  855.             row_ptr[j + 3] = color & 0xFF;
  856.           }
  857.         }
  858.       }
  859.       else
  860.       {
  861.         for (col = j = 0; col < handle->width; col++, j += png_stride)
  862.         {
  863.           color = (int)floor(line_data[col][RED]   * 65535.0);
  864.  
  865.           row_ptr[j] = color >> 8;
  866.           row_ptr[j + 1] = color & 0xFF;
  867.  
  868.           color = (int)floor(line_data[col][GREEN] * 65535.0);
  869.  
  870.           row_ptr[j + 2] = color >> 8;
  871.           row_ptr[j + 3] = color & 0xFF;
  872.  
  873.           color = (int)floor(line_data[col][BLUE]  * 65535.0);
  874.  
  875.           row_ptr[j + 4] = color >> 8;
  876.           row_ptr[j + 5] = color & 0xFF;
  877.  
  878.           /* Handle Alpha here if needed */
  879.           if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  880.           {
  881.             color = 65535 - (int)floor(line_data[col][TRANSM]  * 65535.0);
  882.  
  883.             row_ptr[j + 6] = color >> 8;
  884.             row_ptr[j + 7] = color & 0xFF;
  885.           }
  886.         }
  887.       }
  888.       break;
  889.  
  890.     default:  /* OutputQuality 9 - 15 */
  891.       /* Handle shifting for arbitrary output bit depth */
  892.       himask = 0xFF ^ ((1 << (16 - opts.OutputQuality)) - 1);
  893.  
  894.       if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
  895.       {
  896.         for (col = j = 0; col < handle->width; col++, j += png_stride)
  897.         {
  898.           color = (int)floor((line_data[col][RED]*0.30 +
  899.                               line_data[col][GREEN]*0.59 +
  900.                               line_data[col][BLUE]*0.11) * 65535.0);
  901.  
  902.           row_ptr[j] = color >> 8;
  903.           row_ptr[j + 1] = color & himask;
  904.  
  905.           if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  906.           {
  907.             color = 65535 - (int)floor(line_data[col][TRANSM] * 65535.0);
  908.  
  909.             row_ptr[j + 2] = color >> 8;
  910.             row_ptr[j + 3] = color & himask;
  911.             row_ptr[j + 3] |= color >> opts.OutputQuality;
  912.           }
  913.         }
  914.       }
  915.       else
  916.       {
  917.         for (col = j = 0; col < handle->width; col++, j += png_stride)
  918.         {
  919.           color = (int)floor(line_data[col][RED]   * 65535.0);
  920.  
  921.           row_ptr[j] = color >> 8;
  922.           row_ptr[j + 1] = color & himask;
  923.  
  924.           color = (int)floor(line_data[col][GREEN] * 65535.0);
  925.  
  926.           row_ptr[j + 2] = color >> 8;
  927.           row_ptr[j + 3] = color & himask;
  928.  
  929.           color = (int)floor(line_data[col][BLUE]  * 65535.0);
  930.  
  931.           row_ptr[j + 4] = color >> 8;
  932.           row_ptr[j + 5] = color & himask;
  933.  
  934.           /* Handle Alpha here if needed */
  935.           if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  936.           {
  937.             color = 65535 - (int)floor(line_data[col][TRANSM]  * 65535.0);
  938.  
  939.             row_ptr[j + 6] = color >> 8;
  940.             row_ptr[j + 7] = color & himask;
  941.             row_ptr[j + 7] |= color >> opts.OutputQuality;
  942.           }
  943.         }
  944.       }
  945.   }
  946.  
  947.   if (setjmp(png_ptr->jmpbuf))
  948.   {
  949.     /* If we get here, we had a problem writing the file */
  950.     fclose(handle->file);
  951.     handle->file = NULL;
  952.  
  953.     Error("Error writing PNG output data to %s.\n", handle->file);
  954.   }
  955.  
  956.   /* Write out a scanline */
  957.   png_write_row(png_ptr, row_ptr);
  958.  
  959.   /* Close and reopen file (if not stdout) for integrity in case we crash */
  960.   if (handle->buffer_size == 0 && !(opts.Options & TO_STDOUT))
  961.   {
  962. #ifndef PNG_WRITE_FLUSH
  963.     fflush(handle->file);
  964. #endif
  965.  
  966.     handle->file = freopen(handle->filename, APPEND_FILE_STRING, handle->file);
  967.   }
  968. }
  969.  
  970.  
  971.  
  972. /*****************************************************************************
  973. *
  974. * FUNCTION      : Read_Png_Line
  975. *
  976. * ARGUMENTS     : FILE_HANDLE *handle; COLOUR *line_data; int *line_number;
  977. *
  978. * MODIFIED ARGS : none
  979. *
  980. * RETURN VALUE  : 1 if no error exit
  981. *
  982. * AUTHOR        : Andreas Dilger
  983. *
  984. * DESCRIPTION
  985. *
  986. *   Read a line of PNG data
  987. *
  988. * CHANGES
  989. *
  990. *   Updated for POV-Ray 3.X - [TIW]
  991. *   Updated to handle interrupted file resuming Sept 1995 - [AED]
  992. *   Updated to support grayscale and alpha together, Oct 1995 - [AED]
  993. *
  994. ******************************************************************************/
  995.  
  996. static int Read_Png_Line(handle, line_data, line_number)
  997. FILE_HANDLE *handle;
  998. COLOUR *line_data;
  999. int *line_number;
  1000. {
  1001.   register int col, j, step;
  1002.  
  1003.   if (setjmp(o_png_ptr->jmpbuf))
  1004.   {
  1005.     /* If we get here, we had a problem reading the file, which probably
  1006.      * means that we have read all the available data, rather than a real
  1007.      * error, but there is no sure way to know.
  1008.      */
  1009.     Status_Info("\n");
  1010.     return(0);
  1011.   }
  1012.  
  1013.   if (setjmp(png_ptr->jmpbuf))
  1014.   {
  1015.     /* If we get here, we had a problem writing the new file */
  1016.     Status_Info("\n");
  1017.  
  1018.     fclose(handle->file);
  1019.     handle->file = NULL;
  1020.     fclose(tmp_fp);
  1021.     tmp_fp = NULL;
  1022.  
  1023.     if (DELETE_FILE(handle->filename) != DELETE_FILE_ERR)
  1024.     {
  1025.       RENAME_FILE(tmp_fname,handle->filename); /* Move original file back */
  1026.     }
  1027.  
  1028.     return(-1);
  1029.   }
  1030.  
  1031.   /* Read in another row if available */
  1032.   png_read_row(o_png_ptr, row_ptr, NULL);
  1033.  
  1034.   /* We won't get here if there was a read error */
  1035.   png_write_row(png_ptr, row_ptr);
  1036.  
  1037.   if (*line_number % 32 == 31)
  1038.     Status_Info(".");
  1039.   
  1040.   /*
  1041.    * We must copy all the values because PNG supplies RGBRGB, but POV-Ray
  1042.    * stores RGB components in separate arrays.  Note that since we have
  1043.    * already written the data out to the temporary file, we only need to
  1044.    * use the top 8 bits for the line_data info as it is only used for
  1045.    * potential screen output.
  1046.    */
  1047.  
  1048.   /* How many bytes in a sample */
  1049.   step = (info_ptr->bit_depth <= 8) ? 1 : 2;
  1050.  
  1051.   if ((info_ptr->color_type & PNG_COLOR_MASK_COLOR) == PNG_COLOR_TYPE_GRAY)
  1052.   {
  1053.     for (col = j = 0; col < handle->width; col++, j += png_stride)
  1054.     {
  1055.       line_data[col][RED] = (DBL)row_ptr[j] / 255.0;
  1056.       line_data[col][GREEN] = (DBL)row_ptr[j] / 255.0;
  1057.       line_data[col][BLUE] = (DBL)row_ptr[j] / 255.0;
  1058.  
  1059.       if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  1060.       {
  1061.         line_data[col][TRANSM] = (DBL)(255 - row_ptr[j + step]) / 255.0;
  1062.       }
  1063.     }
  1064.   }
  1065.   else
  1066.   {
  1067.     for (col = j = 0; col < handle->width; col++, j += png_stride)
  1068.     {
  1069.       line_data[col][RED] = (DBL)row_ptr[j] / 255.0;
  1070.       line_data[col][GREEN] = (DBL)row_ptr[j + step] / 255.0;
  1071.       line_data[col][BLUE] = (DBL)row_ptr[j + 2*step] / 255.0;
  1072.  
  1073.       if (info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  1074.       {
  1075.         line_data[col][TRANSM] = (DBL)(255 - row_ptr[j + 3*step]) / 255.0;
  1076.       }
  1077.     }
  1078.   }
  1079.  
  1080.   /* Note that line_number is the line number of the completed row, not
  1081.    * the next row!
  1082.    */
  1083. #if defined(PNG_READ_oFFS_SUPPORTED)
  1084.   *line_number = info_ptr->y_offset + png_ptr->row_number - 1;
  1085. #else
  1086.   *line_number = png_ptr->row_number - 1;
  1087. #endif
  1088.  
  1089.   return(1);
  1090. }
  1091.  
  1092.  
  1093.  
  1094. /*****************************************************************************
  1095. *
  1096. * FUNCTION      : Close_Png_File
  1097. *
  1098. * ARGUMENTS     : FILE_HANDLE *handle
  1099. *
  1100. * MODIFIED ARGS : handle
  1101. *
  1102. * RETURN VALUE  : none
  1103. *
  1104. * AUTHOR        : Andreas Dilger
  1105. *
  1106. * DESCRIPTION
  1107. *
  1108. *   Write any chunks coming after image (eg comments), and free all the
  1109. *   memory associated with the PNG IO streams.  Will output some rendering
  1110. *   stats and info into tEXt chunks if POV_COMMENTS is #defined, and will
  1111. *   also record the rendering time if CTIME is #defined.
  1112. *
  1113. * CHANGES
  1114. *
  1115. *   Updated for POV-Ray 3.X - [TIW]
  1116. *
  1117. ******************************************************************************/
  1118.  
  1119. static void Close_Png_File(handle)
  1120. FILE_HANDLE *handle;
  1121. {
  1122. #ifdef POV_COMMENTS
  1123.   int n, index = - 1;
  1124.   png_text *text_ptr = NULL;
  1125.   char allocated[NTEXT];        /* Boolean array if text is MALLOCed */
  1126.   char bigtext[MAXTEXT];        /* Large temporary string to print into */
  1127. # ifdef CAMERA
  1128.   CAMERA *Camera = Frame.Camera;
  1129. # endif
  1130. #ifdef CTIME
  1131.   char months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun",
  1132.                    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"};
  1133. #endif
  1134. #endif
  1135.  
  1136.   /* Why are we here? */
  1137.  
  1138.   if (handle->file == NULL)
  1139.   {
  1140.     return;
  1141.   }
  1142.  
  1143.   if (handle->mode == WRITE_MODE || handle->mode == APPEND_MODE)
  1144.   {
  1145.     if (png_ptr != NULL)
  1146.     {
  1147.       if (setjmp(png_ptr->jmpbuf))
  1148.       {
  1149.         /* If we get here, we had a problem writing the file */
  1150.  
  1151.         png_destroy_write_struct(&png_ptr, &info_ptr);
  1152.  
  1153.         if (row_ptr != NULL)
  1154.         {
  1155.           POV_FREE(row_ptr);
  1156.           row_ptr = NULL;
  1157.         }
  1158.  
  1159.         if (handle->file != NULL)
  1160.         {
  1161.           fclose (handle->file);
  1162.           handle->file = NULL;
  1163.         }
  1164.  
  1165.         if (handle->buffer != NULL)
  1166.         {
  1167.           POV_FREE(handle->buffer);
  1168.           handle->buffer = NULL;
  1169.         }
  1170.  
  1171.         Error("Error writing PNG file.");
  1172.       }
  1173.  
  1174.       if(png_ptr->row_number < png_ptr->num_rows)
  1175.       {
  1176.          /* finished prematurely - trick into thinking done*/
  1177.          png_ptr->num_rows = png_ptr->row_number;
  1178.          png_write_finish_row(png_ptr);
  1179.       }
  1180.  
  1181. #ifdef POV_COMMENTS /* temporarily skip comment writing code */
  1182.       if (info_ptr != NULL)
  1183.       {
  1184. #if defined(PNG_WRITE_tIME_SUPPORTED)
  1185.         png_convert_from_time_t(&info_ptr->mod_time, tstart);
  1186.         info_ptr->valid = PNG_INFO_tIME;
  1187. #endif /* PNG_WRITE_tIME_SUPPORTED */
  1188.  
  1189. #if defined(PNG_WRITE_tEXt_SUPPORTED)
  1190.         text_ptr = (png_text *)POV_MALLOC(NTEXT*sizeof(png_text), "PNG comment structure");
  1191.  
  1192.         /* Init allocation flags. */
  1193.         for (n = 0; n < NTEXT; n++)
  1194.         {
  1195.           allocated[n] = FALSE;
  1196.           text_ptr[n].compression = - 1;
  1197.         }
  1198.  
  1199. #ifdef TRACER
  1200.         text_ptr[++index].key = "Author";
  1201.         text_ptr[index].text = TRACER;
  1202.         text_ptr[index].text_length = strlen(text_ptr[index].text);
  1203. #endif
  1204.  
  1205. #ifdef COPYRIGHT
  1206.         text_ptr[++index].key = "Copyright";
  1207.         /* 0xA9 is the ISO-8859-1 (used in PNG tEXt) copyright character */
  1208.         sprintf(bigtext, "Copyright %c %d %s", 0xA9, info_ptr->mod_time.year,
  1209.                                                COPYRIGHT);
  1210.         text_ptr[index].text_length = strlen(bigtext);
  1211.         text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
  1212.         strcpy(text_ptr[index].text, bigtext);
  1213.         allocated[index] = TRUE;
  1214.         if (text_ptr[index].text_length > 200) /* Compress if long copyright */
  1215.           text_ptr[index].compression = 0;
  1216. #endif
  1217.  
  1218. #ifdef CTIME
  1219.         /* Print the image "creation" time in RFC 1123 format */
  1220.         text_ptr[++index].key = "Creation Time";
  1221.         sprintf(bigtext, "%02d %3s %4d %02d:%02d:%02d GMT",
  1222.                 info_ptr->mod_time.day, months[info_ptr->mod_time.month],
  1223.                 info_ptr->mod_time.year, info_ptr->mod_time.hour, 
  1224.                 info_ptr->mod_time.minute, info_ptr->mod_time.second);
  1225.         text_ptr[index].text_length = strlen(bigtext);
  1226.         text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
  1227.         strcpy(text_ptr[index].text, bigtext);
  1228.         allocated[index] = TRUE;
  1229. #endif
  1230.  
  1231.         text_ptr[++index].key = "Source";
  1232.         sprintf(bigtext, "Persistence of Vision(tm) Ray Tracer v%s%s",
  1233.                 POV_RAY_VERSION, COMPILER_VER);
  1234.         text_ptr[index].text_length = strlen(bigtext);
  1235.         text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
  1236.         strcpy(text_ptr[index].text, bigtext);
  1237.         allocated[index] = TRUE;
  1238.  
  1239.         if (!(opts.Options & FROM_STDIN))
  1240.         {
  1241.           text_ptr[++index].key = "Input File";
  1242.           text_ptr[index].text = opts.Input_File_Name;
  1243.           text_ptr[index].text_length = strlen(text_ptr[index].text);
  1244.         }
  1245.  
  1246. #ifdef CAMERA
  1247.         text_ptr[++index].key = "POV Camera";
  1248.         sprintf(bigtext, "Location:   %7g %7g %7g\n"
  1249.           "           Direction:  %7g %7g %7g\n"
  1250.           "           Up:         %7g %7g %7g\n"
  1251.           "           Right:      %7g %7g %7g\n"
  1252.           "           Sky:        %7g %7g %7g",
  1253.           Camera->Location[X], Camera->Location[Y], Camera->Location[Z],
  1254.           Camera->Direction[X], Camera->Direction[Y], Camera->Direction[Z],
  1255.           Camera->Up[X], Camera->Up[Y], Camera->Up[Z],
  1256.           Camera->Right[X], Camera->Right[Y], Camera->Right[Z],
  1257.           Camera->Sky[X], Camera->Sky[Y], Camera->Sky[Z]);
  1258.         text_ptr[index].text_length = strlen(bigtext);
  1259.         text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
  1260.         strcpy(text_ptr[index].text, bigtext);
  1261.         allocated[index] = TRUE;
  1262. #endif
  1263.  
  1264.         if (opts.FrameSeq.Clock_Value != 0)
  1265.         {
  1266.           text_ptr[++index].key = "POV Clock";
  1267.           sprintf(bigtext, "%g", opts.FrameSeq.Clock_Value);
  1268.           text_ptr[index].text_length = strlen(bigtext);
  1269.           text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
  1270.           strcpy(text_ptr[index].text, bigtext);
  1271.           allocated[index] = TRUE;
  1272.         }
  1273.  
  1274.         if (opts.Quality != 9)
  1275.         {
  1276.           text_ptr[++index].key = "Rendering Quality";
  1277.           sprintf(bigtext, "%d", opts.Quality);
  1278.           text_ptr[index].text_length = strlen(bigtext);
  1279.           text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
  1280.           strcpy(text_ptr[index].text, bigtext);
  1281.           allocated[index] = TRUE;
  1282.         }
  1283.  
  1284.         text_ptr[++index].key = "Rendering Time";
  1285.         sprintf(bigtext, "%g s", trender);
  1286.         text_ptr[index].text_length = strlen(bigtext);
  1287.         text_ptr[index].text = (char *)POV_MALLOC(text_ptr[index].text_length + 1, "PNG comment");
  1288.         strcpy(text_ptr[index].text, bigtext);
  1289.         allocated[index] = TRUE;
  1290.  
  1291.         info_ptr->num_text = index + 1;
  1292.         info_ptr->max_text = NTEXT;
  1293.         info_ptr->text = text_ptr;
  1294. #endif  /* PNG_WRITE_tEXt_SUPPORTED */
  1295.       }
  1296. #endif  /* POV_COMMENTS */ 
  1297.  
  1298.       png_write_end(png_ptr, info_ptr);
  1299.       png_destroy_write_struct(&png_ptr, &info_ptr);
  1300.  
  1301. #ifdef POV_COMMENTS
  1302.       if (text_ptr != NULL)
  1303.       {
  1304.         for (n = 0; n <= index; n++)
  1305.         {
  1306.           if (allocated[n])
  1307.           {
  1308.             POV_FREE(text_ptr[n].text);
  1309.           }
  1310.         }
  1311.  
  1312.         POV_FREE(text_ptr);
  1313.         text_ptr = NULL;
  1314.       }
  1315. #endif /* POV_COMMENTS */
  1316.  
  1317.     }
  1318.  
  1319.     if (row_ptr != NULL)
  1320.     {
  1321.       POV_FREE(row_ptr);
  1322.       row_ptr = NULL;
  1323.     }
  1324.  
  1325.     if (handle->file != NULL && !(opts.Options & TO_STDOUT))
  1326.     {
  1327.       fclose (handle->file);
  1328.       handle->file = NULL;
  1329.     }
  1330.  
  1331.     if (handle->buffer != NULL)
  1332.     {
  1333.       POV_FREE(handle->buffer);
  1334.       handle->buffer = NULL;
  1335.     }
  1336.   }
  1337.   else /* READ_MODE */
  1338.   {
  1339.     if (o_png_ptr != NULL)
  1340.     {
  1341.       png_destroy_read_struct(&o_png_ptr, (png_infopp)NULL, (png_infopp)NULL);
  1342.     }
  1343.   }
  1344. }
  1345.  
  1346.  
  1347. /*****************************************************************************
  1348. *
  1349. * FUNCTION      : Read_Png_Image
  1350. *
  1351. * ARGUMENTS     : IMAGE *Image; char *name;
  1352. *
  1353. * MODIFIED ARGS : Image
  1354. *
  1355. * RETURN VALUE  : none
  1356. *
  1357. * AUTHOR        : Andreas Dilger
  1358. *
  1359. * DESCRIPTION
  1360. *
  1361. *   Reads a PNG image into an RGB image buffer
  1362. *
  1363. * CHANGES
  1364. *
  1365. *   Updated for POV-Ray 3.X - [TIW]
  1366. *   Updated to allow grayscale and alpha together, Oct 1995 - [AED]
  1367. *   Fixed palette size for grayscale images with bit-depth <= 8, Nov 1995 [AED]
  1368. *   Changed how grayscale images > 8bpp are stored based on use, Nov 1995 [AED]
  1369. *
  1370. ******************************************************************************/
  1371.  
  1372. void Read_Png_Image(Image, name)
  1373. IMAGE *Image;
  1374. char *name;
  1375. {
  1376.   unsigned int width, height;
  1377.   int row, col, j;
  1378.   int stride;
  1379.   FILE *filep;
  1380.   IMAGE_LINE *line_data;
  1381.   png_struct *r_png_ptr;
  1382.   png_info *r_info_ptr;
  1383.   png_byte **row_ptrs;
  1384.  
  1385.   /* Start by trying to open the file */
  1386.  
  1387.   if ((filep = Locate_File(name, READ_FILE_STRING, ".png", ".PNG",TRUE)) == NULL)
  1388.   {
  1389.     Error("Error opening PNG file.\n");
  1390.   }
  1391.  
  1392.   if ((r_png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING,
  1393.                    (png_voidp)TRUE, png_pov_err, png_pov_warn)) == NULL ||
  1394.       (r_info_ptr = png_create_info_struct(r_png_ptr)) == NULL)
  1395.   {
  1396.     Error("Error allocating PNG data structures");
  1397.   }
  1398.  
  1399.   if (setjmp(r_png_ptr->jmpbuf))
  1400.   {
  1401.     /* If we get here, we had a problem reading the file */
  1402.  
  1403.     png_destroy_read_struct(&r_png_ptr, &r_info_ptr, (png_infopp)NULL);
  1404.     Error("Error reading PNG image.");
  1405.   }
  1406.  
  1407.   /* set up the input control */
  1408.  
  1409.   png_init_io(r_png_ptr, filep);
  1410.  
  1411.   /* read the file information */
  1412.  
  1413.   png_read_info(r_png_ptr, r_info_ptr);
  1414.  
  1415.   width = r_info_ptr->width;
  1416.   height = r_info_ptr->height;
  1417.  
  1418.   Image->iwidth = width;
  1419.   Image->iheight = height;
  1420.   Image->width = (DBL)width;
  1421.   Image->height = (DBL)height;
  1422.  
  1423.   /* Allocate buffers for the image */
  1424.   stride = 1;
  1425.  
  1426.   if (r_info_ptr->color_type == PNG_COLOR_TYPE_PALETTE)
  1427.   {
  1428.     IMAGE_COLOUR *cmap;
  1429.     png_color *png_cmap;
  1430.     int cmap_len = r_info_ptr->num_palette;
  1431.     int index;
  1432.  
  1433.     Image->Colour_Map_Size = cmap_len;
  1434.  
  1435.     cmap = (IMAGE_COLOUR *)POV_MALLOC(cmap_len*sizeof(IMAGE_COLOUR), "PNG image color map");
  1436.  
  1437.     Image->Colour_Map = cmap;
  1438.     png_cmap = r_info_ptr->palette;
  1439.  
  1440.     for (index = 0; index < cmap_len; index++)
  1441.     {
  1442.       cmap[index].Red = png_cmap[index].red;
  1443.       cmap[index].Green = png_cmap[index].green;
  1444.       cmap[index].Blue = png_cmap[index].blue;
  1445.       cmap[index].Filter = 0;
  1446.       cmap[index].Transmit = 0;
  1447.     }
  1448.  
  1449.     if (r_info_ptr->valid & PNG_INFO_tRNS)
  1450.     {
  1451.       for (index = 0; index < r_info_ptr->num_trans; index++)
  1452.         cmap[index].Transmit = 255 - r_info_ptr->trans[index];
  1453.     }
  1454.  
  1455.     Image->data.map_lines = (unsigned char **)
  1456.       POV_MALLOC(height * sizeof(unsigned char *), "PNG image");
  1457.  
  1458.     /* tell pnglib to expand data to 1 pixel/byte */
  1459.     png_set_packing(r_png_ptr);
  1460.   }
  1461.   else if (r_info_ptr->color_type == PNG_COLOR_TYPE_GRAY &&
  1462.            r_info_ptr->bit_depth <= 8)
  1463.   {
  1464.     IMAGE_COLOUR *cmap;
  1465.     int cmap_len;
  1466.     int index;
  1467.     
  1468.     Image->Colour_Map_Size = cmap_len = 1 << r_info_ptr->bit_depth;
  1469.     
  1470.     cmap = (IMAGE_COLOUR *)POV_MALLOC(cmap_len*sizeof(IMAGE_COLOUR), "PNG image color map");
  1471.  
  1472.     Image->Colour_Map = cmap;
  1473.  
  1474.     for (index = 0; index < cmap_len; index++)
  1475.     {
  1476.       cmap[index].Red =
  1477.       cmap[index].Green =
  1478.       cmap[index].Blue = index;
  1479.       cmap[index].Filter = 0;
  1480.       cmap[index].Transmit = 0;
  1481.     }
  1482.  
  1483.     if (r_info_ptr->valid & PNG_INFO_tRNS)
  1484.     {
  1485.       for (index = 0; index < r_info_ptr->num_trans; index++)
  1486.         cmap[index].Transmit = 255 - r_info_ptr->trans[index];
  1487.     }
  1488.  
  1489.     Image->data.map_lines = (unsigned char **)
  1490.       POV_MALLOC(height * sizeof(unsigned char *), "PNG image");
  1491.     
  1492.     /* tell pnglib to expand data to 1 pixel/byte */
  1493.     png_set_packing(r_png_ptr);
  1494.   }
  1495.   else if (r_info_ptr->color_type == PNG_COLOR_TYPE_GRAY ||
  1496.            r_info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA)
  1497.   {
  1498.     Image->Colour_Map = NULL;
  1499.     
  1500.     Image->data.rgb_lines = (IMAGE_LINE *)
  1501.       POV_MALLOC(height * sizeof(IMAGE_LINE), "PNG image");
  1502.  
  1503.     if (r_info_ptr->color_type == PNG_COLOR_TYPE_GRAY)
  1504.     {
  1505.       stride = 2;
  1506.     }
  1507.     else if (r_info_ptr->bit_depth <= 8) /* PNG_COLOR_TYPE_GRAY_ALPHA */
  1508.     {
  1509.       /* tell pnglib to expand data to 1 pixel/byte */
  1510.       png_set_packing(r_png_ptr);
  1511.       stride = 2;
  1512.     }
  1513.     else                                  /* PNG_COLOR_TYPE_GRAY_ALPHA */
  1514.     {
  1515.       stride = 4;
  1516.     }
  1517.   }
  1518.   else if (r_info_ptr->color_type == PNG_COLOR_TYPE_RGB ||
  1519.       r_info_ptr->color_type == PNG_COLOR_TYPE_RGB_ALPHA)
  1520.   {
  1521.     Image->Colour_Map = NULL;
  1522.     
  1523.     /* tell pnglib to strip 16 bit depth files down to 8 bits */
  1524.     if (r_info_ptr->bit_depth > 8)
  1525.     {
  1526.       if (opts.Options & VERBOSE)
  1527.         Warning(0.0,"\nConverting PNG image map to 8 bits/sample from higher bit depth.");
  1528.       png_set_strip_16(r_png_ptr);
  1529.     }
  1530.     
  1531.     Image->data.rgb_lines = (IMAGE_LINE *)
  1532.     POV_MALLOC(height * sizeof(IMAGE_LINE), "PNG image");
  1533.  
  1534.     if (r_info_ptr->color_type == PNG_COLOR_TYPE_RGB)
  1535.       stride = 3;
  1536.     else                               /* PNG_COLOR_TYPE_RGB_ALPHA */
  1537.       stride = 4;
  1538.   }
  1539.   else                                 /* Unknown PNG type */
  1540.   {
  1541.     Error("Unsupported color type %d in PNG image.\n", r_info_ptr->color_type);
  1542.   }
  1543.   
  1544.   /* Tell pnglib to handle the gamma conversion for you.  Note that
  1545.    * GammaFactor * DisplayFactor = assumed_gamma, so we are converting
  1546.    * images into the "internal gamma" space of POV (rather than to a
  1547.    * gamma of 1.0) to avoid doing gamma correction on image maps twice for
  1548.    * those scene files which don't have a gamma of 1.0.  For POV 3.0,
  1549.    * we will only do input gamma conversion on those files which will be
  1550.    * used as image maps, and the other types will load the raw pixel values.
  1551.    */
  1552. #if defined(PNG_READ_GAMMA_SUPPORTED) && defined(PNG_READ_gAMA_SUPPORTED)
  1553.   if (r_info_ptr->valid & PNG_INFO_gAMA && (Image->Image_Type & IMAGE_FTYPE))
  1554.   {
  1555.     png_set_gamma(r_png_ptr, opts.GammaFactor*opts.DisplayGamma,
  1556.                                                           r_info_ptr->gamma);
  1557.   }
  1558. #endif /* PNG_READ_GAMMA_SUPPORTED and PNG_READ_gAMA_SUPPORTED */
  1559.     
  1560.   png_set_interlace_handling(r_png_ptr);
  1561.   png_read_update_info(r_png_ptr, r_info_ptr);
  1562.  
  1563.   /* Allocate row buffers for the input */
  1564.   row_ptrs = (png_byte **)POV_MALLOC(height*sizeof(png_byte *), "PNG image");
  1565.   
  1566.   for (row = 0; row < height; row++)
  1567.   {
  1568.     row_ptrs[row] = (png_byte *)POV_MALLOC(r_info_ptr->rowbytes, "PNG image line");
  1569.   }
  1570.   
  1571.   /* Read in the entire image */
  1572.   png_read_image(r_png_ptr, row_ptrs);
  1573.   
  1574.   /* We must copy all the values because PNG supplies RGBRGB, but POV-Ray
  1575.    * stores RGB components in separate arrays
  1576.    */
  1577.   for (row = 0; row < height; row++)
  1578.   {
  1579.     if (Image->Colour_Map == NULL)
  1580.     {
  1581.       line_data = &Image->data.rgb_lines[row];
  1582.  
  1583.       line_data->red = (unsigned char *)POV_MALLOC(width, "PNG image line");
  1584.       line_data->green = (unsigned char *)POV_MALLOC(width, "PNG image line");
  1585.       line_data->blue = (unsigned char *)POV_MALLOC(width, "PNG image line");
  1586.  
  1587.       if (r_info_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  1588.       {
  1589.         line_data->transm = (unsigned char *)POV_MALLOC(width,"PNG image line");
  1590.       }
  1591.       else
  1592.       {
  1593.         line_data->transm = NULL;
  1594.       }
  1595.       
  1596.       /* 8-bit grayscale image with a full alpha channel. Since the paletted
  1597.        * images don't support different transparencies for each pixel, we
  1598.        * have to make this a full-color image.
  1599.        */
  1600.       if (r_info_ptr->color_type == PNG_COLOR_TYPE_GRAY_ALPHA &&
  1601.           r_info_ptr->bit_depth <= 8)
  1602.       {
  1603.         for (col = j = 0; col < width; col ++, j += stride)
  1604.         {
  1605.           line_data->red[col] =
  1606.           line_data->green[col] =
  1607.           line_data->blue[col] = row_ptrs[row][j];
  1608.           line_data->transm[col] = 255 - row_ptrs[row][j + 1];
  1609.         }
  1610.       }
  1611.       /* For 16 bit PNG heightfields, we need to know if the map will be used
  1612.        * for an image (in which case we want to store the values as grays, or
  1613.        * if we want to use it as a heightfield (in which case we need to store
  1614.        * it in the MSB-read, LSB-green format that POV uses to store 16-bit
  1615.        * heightfields.
  1616.        */
  1617.       else if ((r_info_ptr->color_type & PNG_COLOR_MASK_COLOR) ==
  1618.                                                           PNG_COLOR_TYPE_GRAY)
  1619.       {
  1620.         if (Image->Image_Type & HF_FTYPE)
  1621.         {
  1622.           for (col = j = 0; col < width; col ++, j += stride)
  1623.           {
  1624.             int red = row_ptrs[row][j];
  1625.             int green = row_ptrs[row][j + 1];
  1626.  
  1627.             line_data->red[col] = red;
  1628.             line_data->green[col] = green;
  1629.             line_data->blue[col] = 0;
  1630.  
  1631.             if (r_png_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  1632.             {
  1633.               line_data->transm[col] = 255 - row_ptrs[row][j + 2];
  1634.             }
  1635.           }
  1636.         }
  1637.         else
  1638.         {
  1639.           for (col = j = 0; col < width; col ++, j += stride)
  1640.           {
  1641.             int red = row_ptrs[row][j];
  1642.   
  1643.             line_data->red[col] = red;
  1644.             line_data->green[col] = red;
  1645.             line_data->blue[col] = red;
  1646.  
  1647.             if (r_png_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  1648.             {
  1649.               line_data->transm[col] = 255 - row_ptrs[row][j + 2];
  1650.             }
  1651.           }
  1652.         }
  1653.       }
  1654.       else /* r_info_ptr->color_type & PNG_COLOR_MASK_COLOR */
  1655.       {
  1656.         for (col = j = 0; col < width; col ++, j += stride)
  1657.         {
  1658.           line_data->red[col] = row_ptrs[row][j];
  1659.           line_data->green[col] = row_ptrs[row][j + 1];
  1660.           line_data->blue[col] = row_ptrs[row][j + 2];
  1661.  
  1662.           if (r_png_ptr->color_type & PNG_COLOR_MASK_ALPHA)
  1663.           {
  1664.             line_data->transm[col] = 255 - row_ptrs[row][j + 3];
  1665.           }
  1666.         }
  1667.       }
  1668.       POV_FREE(row_ptrs[row]);
  1669.     }
  1670.     else
  1671.     {
  1672.       Image->data.map_lines[row] = row_ptrs[row];
  1673.     }
  1674.   }
  1675.  
  1676.   /* Clean up the rest of the PNG memory and such */
  1677.  
  1678.   POV_FREE(row_ptrs);
  1679.  
  1680.   /* read the rest of the file, getting any additional chunks in png_info */
  1681.  
  1682.   png_read_end(r_png_ptr, r_info_ptr);
  1683.  
  1684.   /* clean up after the read, and free any memory allocated */
  1685.  
  1686.   png_destroy_read_struct(&r_png_ptr, &r_info_ptr, (png_infopp)NULL);
  1687.  
  1688.   fclose(filep);
  1689. }
  1690.